<?php
namespace app\admin\middleware;
use think\facade\Config;
use think\facade\Request;
use think\facade\Cache;
use think\facade\Cookie;
use think\captcha\Captcha;
use app\facade\CacheTool;

/**
 * 登陆接口通用数据配置检测
 */
class AdminLoginCheck
{
	/**
	 * handle  重写处理请求对象的操作函数
	 * @param  object Request $request 请求对象
	 * @param  object \Closure $next 响应对象
	 * @return array 错误返回的信息
	 *		code 返回码
	 *  	msg 返回信息
	 *	  	data 返回数据
	 * @return  object 响应对象
	 */
	public function handle($request, \Closure $next)
	{
		// 获取控制器信息
		$module = strtolower(Request::module());
		$controller = strtolower(Request::controller());
		$function = strtolower(Request::action());

		// 对请求接口目标路径进行处理如果带有扩展名则将扩展名消除
		if (preg_match("/^[\w][\w]{0,31}[\.][a-zA-Z]{1,10}$/", $function)) $function = explode('.',$function)[0];
		else $function = $function;
		// 获取配置
		$config = Config::get($controller.'.');
		// 对页面 访问进行检测
		if (true === $config['page'])
		{
			$res = self::pageCheck($function,$config['page_switch']);
			if (true !== $res) exit(json_encode(['code'=>1,'msg'=>'Page_Disable','data'=>'']) );
		}
		// 对api 访问进行检测
		if (true === $config['api'])
		{
			$res = self::apiCheck($function,$config['api_switch']);
			if (true !== $res) exit(json_encode(['code'=>1,'msg'=>'Api_Disable','data'=>'']) );
		}
		// 对 request 访问进行检测
		if (true === $config['request'])
		{
			$res = self::requestCheck($function,$config['request_switch']);
			if (true !== $res) exit(json_encode(['code'=>1,'msg'=>'Requert_Disable','data'=>'']) );
		}
		// 如果是登录接口的情况下
		if ($function === 'login')
		{

			// 对ip进行规则访问，查看是否是允许的ip 进行访问
			if (true === $config['address_limit'])
			{
				$res = self::ipCheck(Request::ip(),$config['address_limit_data']);
				if (true !== $res) exit(json_encode(['code'=>1,'msg'=>'Address_Not_Access','data'=>'']));
			}
			// 请求次数检测，检测请求次数防止连续攻击
			if (true === $config['request_number'])
			{
				$res = self::requestsNumberCheck($request,$config['request_number_data']);
				if (true !== $res) exit(json_encode(['code'=>1,'msg'=>$res,'data'=>'']));
			}
			// 验证码检测访问，查看验证马配置是否开启如果开启则生效验证码
			if (true === $config['code'])
			{
				$res = self::codeCheck($request);
				if (true !== $res) exit(json_encode(['code'=>1,'msg'=>lang('Code_Validation_Fails'),'data'=>'']));
			}
			// 检测表单令牌配置是否生效.如果生效则进行令牌检测
			if (true === $config['repeat_token'])
			{
				$res = self::tokenCheck();
				if (true !== $res) exit(json_encode(['code'=>1,'msg'=>'Token_Data_Failure','data'=>'']));
			}
		}	
		// 格式化与处理前台参数
		$request = self::loginParamDispose($function,$request,$config);
		// 继续执行进入到控制器
		return $next($request);
	}

	protected static function pageCheck($scene,$config)
	{
		// 场景判断
		switch ($scene)
		{
			case $scene:
				// 键是否存在于配置中
				$page = key_exists($scene,$config);
				// 如果键存在则取出数据 否则返回
				if (true === $page) $switch = $config[$scene];
				else {$res = '2';break;}
				// 页面打开或者关闭的状态
				if (true === $switch) {$res = '0';break;}
				else {$res = '1';break;}
		}
		// 结果反馈
		switch ($res)
		{
			case '0': return true; break;// 页面开启状态
			case '1': return false; break;// 页面关闭状态
			case '2': return true; break;// 页面不存在状态 不做任何处理 可能需要指向404更好
		}
	}

	protected static function apiCheck($scene,$config)
	{
		// 场景判断
		switch ($scene)
		{
			case $scene:
				// 键是否存在于配置中
				$api = key_exists($scene,$config);
				// 如果键存在则取出数据 否则返回
				if (true === $api) $switch = $config[$scene];
				else {$res = '2';break;}
				// api打开或者关闭的状态
				if (true === $switch) {$res = '0';break;}
				else {$res = '1';break;}
		}
		// 结果反馈
		switch ($res)
		{
			case '0': return true; break;// Api开启状态
			case '1': return false; break;// Api关闭状态
			case '2': return true; break;// Api不存在状态 
		}
	}

	/**
	* requestCheck 请求类型验证
	* @param  string $scene 根据路径传入方法名称
	* @return bool 验证用户访问的接口是否符合预设的请求要求
	*/
	protected static function requestCheck($scene,$config)
	{
		$request = key_exists($scene,$config);
		// 请求是否存在
		if (true === $request)
		{
			switch ($scene)
			{
				// 页面地址请求验证
				case 'index':
					if (true === $config[$scene]) { if (Request::isGet()) $res = '0'; else $res = '1'; }
					else $res = '2'; break;
				case 'code': 
					if (true === $config[$scene]) { if (Request::isGet()) $res = '0'; else $res = '1'; }
					else $res = '2'; break;
				case 'login': 
					if (true === $config[$scene]) { if (Request::isPost()) $res = '0'; else $res = '1'; }
			}
		}
		else
		{
			// 架构时候 指向数字1 方便调试报错
			$res = '3';//请求不存在处理 期望跳转到404？
		}
		// 结果反馈
		switch ($res)
		{
			case '0': return true; break;// 请求验证通过状态
			case '1': return false; break;// 请求验证失败状态
			case '2': return true; break;// 请求验证被关闭状态 
			case '3': return true; break;// 不存在的接口请求状态 
		}
	}

	/**
	 * loginAddressDispose 地址是否允许访问
	 * @param  string $address 需要传入一个address地址
	 * @return string     返回错误信息
	 * @return bool 	检测正确返回真
	 */
	protected static function ipCheck($address,$config)
	{
		// 如果配置信息address列表为空则返回不能访问
		if (empty($address)) return false;
		// 查看值是否在数组中
		if (true === in_array($address,$config)) return true; 
		// 如果继续执行下去依然没有 返回不能访问
		return false;
	}

	/**
	 * requestsNumberCheck 登录接口次数限制查看配置是否有误
	 * @return bool 验证的结果
	 */
	protected static function requestsNumberCheck($request,$config)
	{
		// 如果ip请求次数被开启的
		if (true == $config['ip_request'])
		{
			// 获取ip限制配置
			$ipconfig = $config['ip_request_config'];
			// 执行缓存规则
			$res = CacheTool::requestRoleSet(request::ip(),$ipconfig['num'],$ipconfig['time'],$ipconfig['tage']);
		}
		// 如果user请求次数被开启的
		if (true == $config['username_request'])
		{
			// 获取 username 限制配置
			$ipconfig = $config['username_request_config'];
			// 对用户名稍微做下处理 芥末欧用户名必须叫user
			if (empty(Request::param('user'))) $user = 'username';
			// 用户名赋值
			else $user = Request::param('user');
			// 执行缓存规则
			$res = CacheTool::requestRoleSet($user,$ipconfig['num'],$ipconfig['time'],$ipconfig['tage']);
		}
		// 如果部位整则返回错误信息
		if (true != $res) return $res;
		// 请求对象参数
		return $res = true;
	}

	/**
	 * loginCodeCheck 地址是否允许访问
	 * @param  string $code 需要传入一个code地址
	 * @return string     返回错误信息
	 * @return bool 	检测正确返回真
	 */
	protected static function codeCheck($request)
	{
		// 获取配置
		$config = Config::get('admin.code_config');
		// 实例化验证码对象
		$captcha = new Captcha($config);
		//  验证码校验检测 错付返回信息
		if(!$captcha->check($request->param('code'))) return false;
		// 正确返回信息
		return true;
	}
	
	/**
	 * 这段要着重处理的内容  整个中间件处理完成之后,就可以 写信的代码了
	 * 表单方重复提交令牌
	 * @return bool           返回真假值
	 */
	protected static function tokenCheck()
	{
		// 获取配置参数
		$config = Config::get('web.repeat_token_config');
		if (!function_exists('tokenCheck_on'))
		{
			// 自设函数
			function tokenCheck_on($config)
			{
				// 产生微秒时间戳
				if (true === $config['microtime']) $token['time'] = microtime();
				// 产生随机数
				if (true === $config['rand']) $token['rand'] = mt_rand(100000,999999);
				// 获取加密钥匙
				$token['key'] =  $config['key'];
				// 获取提交的内容参数
				$token['param'] = json_encode(Request::param());
				// 设置合并参数变量
				$merge = '';
				// 循环加密内容
				foreach ($token as $key => $val) { $merge .= $val; }
				// 数据加密
				$data = md5($merge);
				// 存储密文信息到cookie 等待读取
				Cookie::set($config['tag'],$data,$config['time']);
				// 设置缓存标
				Cache::tag($config['tag'])->set($data,$token,$config['time']);
				// 获取cookie中的token数据
				return $token;
			}
		}
		if (!function_exists('tokenCheck_de'))
		{
			// 自解函数
			function tokenCheck_de($token,$config)
			{
				// 获取id
				$id = Cookie::get($config['tag']);
				// 获取缓存数据
				$data = Cache::tag($config['tag'])->get($id);
				// 设置验证参数变量
				$verify = '';
				// 循环cookie内容
				foreach ($data as $key => $val) $verify .= $val;
				// md5解密验证
				$verify = md5($verify);
				// 如果单次传入相等则通过
				if ($id === $verify) return true;
				else return false;
			}
		}
		$token = tokenCheck_on($config);
		$res = tokenCheck_de($token,$config);
		return $res;
	}

	/**
	 * loginParamDispose post内容与格式处理
	 * @param  string $scene 需要前往的接口名称
	 * @param  object $request 请求的对象内容
	 * @return object 返回处理过的请求对象
	 */
	protected static function loginParamDispose($scene,$request,$config)
	{
		switch ($scene)
		{
			// 登陆页面
			case 'index':
				break;
			// 登陆接口请求参数处理
			case 'login':
				// 前台用户传入的参数进行调整转换
				$request->username = $request->param('user');
				$request->password = $request->param('pass');
				$request->captcha = $request->param('code');
				// 对记住我进行处理
				if (null === $request->param('remember')) $request->keep = 'shut';
				else $request->keep = 'open';
				// 对登录协议进行处理
				if (null === $request->param('agree')) $request->agreement = 'shut';
				else $request->agreement = 'open';
				// 对数据库检测进行配置预处理
				if (true === $config['db_check']) $request->loginsql = 'open';
				else $request->loginsql = 'shut';
				// 查看token是否被开启如果
				if (true === Config::get('web.oauth2')) $request->oauth2 = 'open';
				else $request->oauth2 = 'shut';
				break;
			// 登陆验证码接口处理
			case 'code':
				break;
			// 重置密码接口参数处理
			case 'resetPassword':
				// 前台用户传入的参数进行调整转换
				$request->username = $request->param('user');
				$request->phone = $request->param('mobile');
				$request->phonecode = $request->param('code');
				$request->password = $request->param('pass');
				$request->repassword = $request->param('repass');
				break;
			// 默认接口或者不存在的场景返回
			default:
				break;
		}
		return $request;
	}
}
